#include "stdafx.h"
#include <QObject>
#include <QString>
#include <QStringList>
#include <algorithm>
#include "cmdparser.h"

namespace rco {

#define INSERT_CMD(str, id)  (m_cmdStrMap.insert(QString::fromWCharArray(str), id))
#define SPACE QChar(' ')

RCMDINFO(1) g_rCOPYInfo = {RCO_ID_COPY, 1, 2, {"cv"}};  // cover
RCMDINFO(1) g_rMOVEInfo = {RCO_ID_MOVE, 1, 2, {"cv"}};  // cover
RCMDINFO(1) g_rDELETEInfo = {RCO_ID_DELETE, 0, 1, {nullptr}};
RCMDINFO(2) g_rMKDIRInfo = {RCO_ID_MKDIR, 2, 1, {"cv", "rn"}}; // cover, rename
RCMDINFO(1) g_rTOUCHInfo = {RCO_ID_TOUCH, 1, 1, {"cv"}};  // cover
RCMDINFO(1) g_rCDInfo = {RCO_ID_CD, 0, 1, {nullptr}};  
RCMDINFO(1) g_rLSInfo = {RCO_ID_LS, 0, 1, {nullptr}};
RCMDINFO(2) g_rDLInfo = {RCO_ID_DL, 2, 2, {"115","c"}}; 
RCMDINFO(1) g_rGETInfo = {RCO_ID_GET, 1, 1, {"rcv"}};  // recusive
RCMDINFO(1) g_rPUTInfo = {RCO_ID_PUT, 0, 1, {nullptr}}; 
RCMDINFO(1) g_rSETPATHInfo = {RCO_ID_SETPATH, 0, 1, {nullptr}};
RCMDINFO(1) g_rQPROCESSInfo = {RCO_ID_QPROCESS, 1, 0, {"loc"}}; // location
RCMDINFO(2) g_rKILLInfo = {RCO_ID_KILL, 2, 1, {"pid", "n"}};   // pid, name
RCMDINFO(4) g_rTHREADInfo = {RCO_ID_THREAD, 4, 1, {"new", "rm", "q", "kill"}}; // new, remove, query, terminate
RCMDINFO(1)	g_rGETPATHInfo = {RCO_ID_GETPATH, 0, 1, {nullptr}};
RCMDINFO(1) g_rSHUTDOWNInfo = {RCO_ID_SHUTDOWN, 1, 1, {"t"}}; // time

const RcoCmdInfoBase* g_rcoCmdInfos[] = {
	BASE(&g_rCOPYInfo),
	BASE(&g_rMOVEInfo),
	BASE(&g_rDELETEInfo),
	BASE(&g_rMKDIRInfo),
	BASE(&g_rTOUCHInfo),
	BASE(&g_rCDInfo),
	BASE(&g_rLSInfo),
	BASE(&g_rDLInfo),
	BASE(&g_rGETInfo),
	BASE(&g_rPUTInfo),
	BASE(&g_rSETPATHInfo),
	BASE(&g_rQPROCESSInfo),
	BASE(&g_rKILLInfo),
	BASE(&g_rTHREADInfo),
	BASE(&g_rGETPATHInfo),
	BASE(&g_rSHUTDOWNInfo)
};

bool RcoCmdParser::Init(fnParamParsedCallback fnCb)
{
	Q_ASSERT(fnCb);

	INSERT_CMD(RCO_COPY, RCO_ID_COPY);
	INSERT_CMD(RCO_MOVE, RCO_ID_MOVE);
	INSERT_CMD(RCO_DELETE, RCO_ID_DELETE);
	INSERT_CMD(RCO_MKDIR, RCO_ID_MKDIR);
	INSERT_CMD(RCO_TOUCH, RCO_ID_TOUCH);
	INSERT_CMD(RCO_CD, RCO_ID_CD);
	INSERT_CMD(RCO_LS, RCO_ID_LS);
	INSERT_CMD(RCO_DL, RCO_ID_DL);
	INSERT_CMD(RCO_GET, RCO_ID_GET);
	INSERT_CMD(RCO_PUT,RCO_ID_PUT);
	INSERT_CMD(RCO_SETPATH, RCO_ID_SETPATH);
	INSERT_CMD(RCO_QPROCESS, RCO_ID_QPROCESS);
	INSERT_CMD(RCO_KILL, RCO_ID_KILL);
	INSERT_CMD(RCO_GETPATH,RCO_ID_GETPATH);
	INSERT_CMD(RCO_SHUTDOWN, RCO_ID_SHUTDOWN);

	m_ppCmdInfos = g_rcoCmdInfos;
	m_fnCb = fnCb;
	return true;
}

int _SkipForwardSpaces(const QString& str, int iBeg = 0)
{
	int i = iBeg;
	for (; i < str.size(); ++i)
	{
		if (str[i] != SPACE)
			break;
	}
	return i;
}

void _RemoveBackwardSpaces(QString& str, int iEnd = -1)
{
	int i = iEnd == -1 ? str.size() : iEnd;
	for (--i; i >= 0; --i)
	{
		if (str[i]== SPACE)
			str.remove(i, 1);
		else
			break;
	}
}

CmdParserResult RcoCmdParser::OnCommandParsing(const QString& strCmd, RcoCommandHead* pRcoCmd, int* pPosParsed)
{
	Q_ASSERT(pRcoCmd);
	QString rstrCmd = strCmd;
	int iCmdBeg = _SkipForwardSpaces(rstrCmd);
	_RemoveBackwardSpaces(rstrCmd);

	CmdParserResult cpr = RCO_CMD_NOTEXIST;
	{
		int iSpace = rstrCmd.indexOf(SPACE, iCmdBeg);
		// no switch or param
		if (iSpace == -1)
			iSpace = rstrCmd.length();

		*pPosParsed = iSpace;
		QString strCmdHead = rstrCmd.mid(iCmdBeg, iSpace - iCmdBeg).toLower();
		auto ite = m_cmdStrMap.find(strCmdHead);
		RCO_CHECK_BOOL(ite != m_cmdStrMap.end());
		cpr = RCO_CMD_OK;
		const RcoCmdInfoBase* pCmdInfo = m_ppCmdInfos[*ite - 1];
		pRcoCmd->ucCid = pCmdInfo->ucCid;
		pRcoCmd->nParam = pCmdInfo->nParam;

		int iSwitchEnd = iSpace;
		if (pCmdInfo->nSwitch > 0)
		{
			int iSwitchBeg = _SkipForwardSpaces(rstrCmd, iSpace);//rstrCmd.indexOf(QChar('-'), iSpace);
			*pPosParsed = iSwitchBeg;
			RCO_CHECK_BOOL(rstrCmd[iSwitchBeg] == QChar('-'));

			if (iSwitchBeg != -1)
			{
				iSwitchEnd = rstrCmd.indexOf(SPACE, iSwitchBeg);
				QString strSwitches = rstrCmd.mid(iSwitchBeg, iSwitchEnd - iSwitchBeg).toLower();
				RCO_CHECK(cpr = OnSwitchParsing(strSwitches, pCmdInfo, pRcoCmd));
			}
		}

		RCO_CHECK_BOOL(iSwitchEnd != -1);	
		
		*pPosParsed = _SkipForwardSpaces(rstrCmd, iSwitchEnd);		
	}
RCO_EXIT:
	return cpr;
}

rco_sid TranslateSwitch(const QStringList& strList, const char** ppStrs, int n)
{
	Q_ASSERT(ppStrs && n > 0);

	rco_sid sid = 0;
	auto end = strList.end();
	for (auto ite = strList.begin(); ite != end; ++ite)
	{
		for (int i = 0; i < n; ++i)
		{
			if (ite->compare(ppStrs[i]) == 0)
				sid |= RCO_CMD_SWITCH(i);
		}
	}
	return sid;
}

CmdParserResult RcoCmdParser::OnSwitchParsing(
	const QString& strSwitch, 
	const RcoCmdInfoBase* pCmdInfo, 
	RcoCommandHead* pRcoCmd)
{
	CmdParserResult cpr = RCO_CMD_SWITCH_ERROR;
	{
		QStringList strList = strSwitch.split(QChar('-'), QString::SkipEmptyParts);
		int size = strList.size();
		RCO_CHECK_BOOL(size != 0);
		int nSwitch = pCmdInfo->nSwitch;
		RCO_CHECK_BOOL(nSwitch != 0);

		const char** ppSwitchStrArray = (const char**)((reinterpret_cast<const RcoCommandInfo<1>*>(pCmdInfo))->ppSwitchStrArray);
		pRcoCmd->ucSwitch = TranslateSwitch(strList, ppSwitchStrArray, pCmdInfo->nSwitch);

		if (pRcoCmd->ucSwitch != 0)
			cpr = RCO_CMD_OK;
	}
RCO_EXIT:
	return cpr;
}

template<class Type>
inline void DeleteElement(Type& t)
{
	delete t;
}

int RcoCmdParser::OnParamParsing(const QString& strCmd, int iBeg, int n, QString params[])
{
	int i = 0;
	int iPos = iBeg;
	int iEnd = 0;
	int len = strCmd.size();
	QChar qcSep;
	
	while(i < n && iPos < len)
	{
		iPos = _SkipForwardSpaces(strCmd, iPos);
		if (strCmd[iPos] == QChar('"'))
		{
			qcSep = QChar('"');

			if (++iPos >= len)
				return i;
		}
		else
			qcSep = SPACE;

		iEnd = strCmd.indexOf(qcSep, iPos);

		if (iEnd == -1)
			iEnd = len;

		params[i].clear();
		params[i].append(strCmd.midRef(iPos, iEnd - iPos));
		iPos = iEnd + 1;
		i += 1;
	}

	return i;
}

int RcoCmdParser::OnParamParsing(const QByteArray& bytes, int iBeg, int n, QString params[])
{
	return OnParamParsing(QString(bytes), iBeg, n, params);
}

}